LoginSignup
hi_lili
@hi_lili

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

非同期処理で検索をした後、IDが取得されません

解決したいこと

index.blade.phpの絞り込みボタンを押下した後、
表示されるレコードのidを取得したいです:bow_tone1:

使用環境
laravel9
composer
MacOS
Ajax
JQuery

LaravelでAjaxとJQueryを利用し、非同期処理を行っております。
非同期で検索処理を行い該当のレコードを表示することはできております。
しかし取得したレコードの詳細、編集画面へ移動することができません:fearful:
「詳細表示ボタン」「編集ボタン」を押下すると下記のエラーがブラウザにて確認されます・・・

発生している問題・エラー

GET http://localhost:8888/products/undefined 404 (Not Found)

Chromeの検証で上記が確認されているため、
該当のidを取得すればいいと思うのですが、うまく取得ができていない状況です。

該当するソースコード

search.js
$(function() {
  $('#search-form').submit(function(event) {
    event.preventDefault();
    
    var csrfToken = $('meta[name="csrf-token"]').attr('content');

    $.ajax({
      url: "http://localhost:8888/product/public/products/search", 
      type: 'GET',
      dataType: "json",
      data: {
        search: $('#search').val(),
        company_id: $('#company').val(),
        priceMin: $('input[name="price_min"]').val(),
        priceMax: $('input[name="price_max"]').val(),
        stockMin: $('input[name="stock_min"]').val(),
        stockMax: $('input[name="stock_max"]').val(),
      }
    }).done((response) => {
        console.log(response);
        var $result  = $('#search-results');
        $result.empty();
        $.each(response.products, function(index, product){
          var productId = product.id;
          var html = `
          <tr>
              <td>${product.product_name}</td>
              <td>${product.company_name}</td>
              <td>${product.price}</td>
              <td>${product.stock}</td>
              <td>${product.comment}</td>
              <td><img src="${product.img_path}" alt="商品画像" width="100"></td>
              <td>
                  <a href="/products/${productId}" class="btn btn-info btn-sm mx-1">詳細表示</a>
                  <a href="/products/${productId}/edit" class="btn btn-primary btn-sm mx-1">編集</a>
                  <form method="POST" action="/products/${productId}" class="d-inline">
                      <input type="hidden" name="_token" value="${csrfToken}">
                      <input type="hidden" name="_method" value="DELETE">
                      <button type="submit" class="btn btn-danger btn-sm mx-1">削除</button>
                  </form>
              </td>
          </tr>`;
          $result.append(html);
      });
      }).fail((error) => {
        console.log('AJAXが失敗しました');
      });
    });
  });

ProductController.php
class ProductController extends Controller
{

    public function index(Request $request){

        $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
        ->paginate(10);
        $company_lists = Company::pluck('company_name', 'id');
        $company = $request->company_id;
    
        return view('products.index', compact('products', 'company_lists', 'company'));
    }

    public function search(Request $request)
    {
        $products = Product::search($request->search, $request->company_id, $request->price_min, $request->price_max, $request->stock_min, $request->stock_max)
        ->paginate(10);
    $company_lists = Company::pluck('company_name', 'id');
    $company_id = $request->company_id;

    $products = $products->map(function ($product) {
        return [
            'product_name' => $product->product_name ?? '',
            'company_name' => $product->company->company_name ?? '',
            'price' => $product->price ?? '',
            'stock' => $product->stock ?? '',
            'comment' => $product->comment ?? '',
            'img_path' => asset($product->img_path) ?? '',
        ];
    });
    
        return response()->json([
            'products' => $products,
            'company_id' => $company_id,
            'company_lists' => $company_lists
        ]);
    }
}
Product.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;


class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'id',
        'product_name',
        'price',
        'stock',
        'company_id',
        'comment',
        'img_path',
    ];

    public function sales()
    {
        return $this->hasMany(Sale::class);
    }

    public function company()
    {
        return $this->belongsTo(Company::class);
    }

    public function getCompany()
    {
        return $this->company;
    }
        

    public static function createProduct($requestData){

        $product = new Product();
        $product->product_name = $requestData['product_name'];
        $product->company_id = $requestData['company_id'];
        $product->price = $requestData['price'];
        $product->stock = $requestData['stock'];
        $product->comment = $requestData['comment'];

        // idフィールドは自動インクリメントなので、明示的に設定する必要はありません。

        if (isset($requestData['img_path'])) {
            $img_path = $requestData['img_path']->store('products', 'public');
            $product->img_path = 'storage/' . $img_path;
        }

        $product->save();
        return $product;
    }
    

   public function scopeSearch($query, $search, $company, $priceMin, $priceMax, $stockMin, $stockMax)
    {
        if ($search) {
        $query->where('product_name', 'LIKE', "%{$search}%");
        }

        if ($company) {
        $query->whereHas('company', function ($query) use ($company) {
            $query->where('company_id', $company);
        });
        }

        if ($priceMin) {
            $query->where('price', '>=', $priceMin);
        }
        
        if ($priceMax) {
            $query->where('price', '<=', $priceMax);
        }
        
        if ($stockMin) {
            $query->where('stock', '>=', $stockMin);
        }
        
        if ($stockMax) {
            $query->where('stock', '<=', $stockMax);
        }

        return $query;
    }

}

index.blade.php
@extends('layouts.app')


@section('content')
<div class="container">
    <h1 class="mb-4">商品情報一覧</h1>

    <a href="{{ route('products.create') }}" class="btn btn-primary mb-3">商品新規登録</a>

    <div class="search mt-5">
    
    <h2>検索条件で絞り込み</h2>
    
    <form action="{{ route('products.index') }}" method="GET" class="row g-3" id="search-form">
        <div class="col-sm-12 col-md-3">
            <input type="text" name="search" class="form-control" placeholder="商品名" value="{{ request('search') }}" id="search">
        </div>
        <div class="col-sm-12 col-md-3">
            <select name="company_id" class="form-control" id="company">
                <option value="">全て</option>
                @foreach($company_lists as $key => $value)
                <option value="{{ $key }}" @if($company == $key) selected="selected" @endif>{{ $value }}</option>
                @endforeach
            </select>
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="price_min" class="form-control" placeholder="価格の下限" value="{{ request('price_min') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="price_max" class="form-control" placeholder="価格の上限" value="{{ request('price_max') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="stock_min" class="form-control" placeholder="在庫数の下限" value="{{ request('stock_min') }}">
        </div>
        <div class="col-sm-12 col-md-3">
            <input type="number" name="stock_max" class="form-control" placeholder="在庫数の上限" value="{{ request('stock_max') }}">
        </div>
        <div class="col-sm-12 col-md-1">
            <button class="btn btn-outline-secondary" type="submit">絞り込み</button>
        </div>
    </form>
</div>

<a href="{{ route('products.index') }}" class="btn btn-success mt-3">検索条件を元に戻す</a>
    <div class="products mt-5" >
        <h2>商品情報</h2>
        <table class="table table-striped" >
            <thead>
                <tr>
                    <th>商品名</th>
                    <th>メーカー</th>
                    <th>価格</th>
                    <th>在庫数</th>
                    <th>コメント</th>
                    <th>商品画像</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody id="search-results">
            @foreach ($products as $product)
                <tr>
                    <td>{{ $product->product_name }}</td>
                    <td>{{ $product->company->company_name }}</td>
                    <td>{{ $product->price }}</td>
                    <td>{{ $product->stock }}</td>
                    <td>{{ $product->comment }}</td>
                    <td><img src="{{ asset($product->img_path) }}" alt="商品画像" width="100"></td>
                    </td>
                    <td>
                        <a href="{{ route('products.show', $product->id) }}" class="btn btn-info btn-sm mx-1">詳細表示</a>
                        <a href="{{ route('products.edit', $product->id) }}" class="btn btn-primary btn-sm mx-1">編集</a>
                        <form method="POST" action="{{ route('products.destroy', $product->id) }}" class="d-inline">
                            @csrf
                            @method('DELETE')
                            <button type="submit" class="btn btn-danger btn-sm mx-1">削除</button>
                        </form>
                    </td>
                </tr>
            @endforeach
            </tbody>
        </table>
    </div>
    
    {{ $products->appends(request()->query())->links() }} 
</div>
@endsection

何卒、ご教授いただけますと幸いです。
よろしくお願い申し上げます:bow_tone1:

0

4Answer

以下の処理でidも返す必要が有りますかね?レスポンスのデータが問題だと思うので、ブラウザの開発者ツールとか、ログに出してみるとかして確認してみてください。
存在しないキー(id)を参照してるので、undefinedになっているかと、、、

$products = $products->map(function ($product) {
return [
'product_name' => $product->product_name ?? '',
'company_name' => $product->company->company_name ?? '',
'price' => $product->price ?? '',
'stock' => $product->stock ?? '',
'comment' => $product->comment ?? '',
'img_path' => asset($product->img_path) ?? '',
];
});

2

Comments

  1. 404 応答が返ってきているのでサーバー側では php のコードが動くところまで行ってないと思いますが?

    エラーメッセージは、

    GET http://localhost:8888/products/undefined 404 (Not Found)

    ということなので、多分 search.js のコード、

    <a href="/products/${productId}" class="btn btn-info btn-sm mx-1">詳細表示</a>
    

    で、productId が undefined になっていて、それをクリックすると存在しない URL が要求されるからだと思います。

非同期で検索処理を行い該当のレコードを表示することはできております。しかし取得したレコードの詳細、編集画面へ移動することができません

「詳細表示ボタン」「編集ボタン」を押下すると下記のエラーがブラウザにて確認されます・・・
GET http://localhost:8888/products/undefined 404 (Not Found)

404 応答が返ってくるということは、ブラウザからの要求は Web サーバーに届いて、Web サーバーで URL に指定されるリソースを探したが見つからなかったということです。ほとんどの場合、原因は指定した URL が間違っているということです。

「詳細表示ボタン」「編集ボタン」というのは search.js がレンダリングする a タグのことですよね。であれば、その href 属性に指定した URL が間違っているのでしょう。まずはそこを調べてください。

エラーメッセージからすると、多分 search.js のコード、

<a href="/products/${productId}" class="btn btn-info btn-sm mx-1">詳細表示</a>

で、productId が undefined になっていて、結果レンダリングされる html は <a href="/products/undefined" ...> となり、その a タグをクリックすると存在しない URL が要求されるから 404 応答になるのだと思います。


【追伸】

ひょっとして上に書いたことは質問者さんはすでに分かっていて、知りたいことは search.js のコード、

var productId = product.id;

・・・のところで何故 productId が undefined になるかですか?

であれば、$.ajax で要求をかけた結果返ってくる JSON 文字列がどうなっているかと、<a href="/products/${productId}" ...> が html にレンダリングされた時どうなればいいかを書いてください。

1

GET http://localhost:8888/products/undefined 404 (Not Found)

URLのidの部分がundefinedになっているので、URLを生成する/products/${productId}に問題があるのでしょう。

search.jsでそのURLを生成しているようですが、productIdproduct.idの値が想定されたものになっているか確認されましたか?

またproductsの情報を返す処理について明記されていませんが、おそらくProductController.phpserachなのだと思います。その部分を見たところ、productのIDが含まれていないように見えます。

        return [
            'product_name' => $product->product_name ?? '',
            'company_name' => $product->company->company_name ?? '',
            'price' => $product->price ?? '',
            'stock' => $product->stock ?? '',
            'comment' => $product->comment ?? '',
            'img_path' => asset($product->img_path) ?? '',
        ];
1

Laravelの質問に共通しているのはとにかく「参考にしている情報を間違えている」
何を見てjQueryでajaxなんて10年以上前の古い使い方してるの?何も知らない初心者がこんなことするわけないので誰かに騙されている。
Laravel9の時代にはもう誰もそんなことしてない。
Laravel9のpackage.jsonには最初からaxiosが含まれてるので使うならaxios。
https://github.com/laravel/laravel/blob/9.x/package.json
Laravel7までbootstrap(css)のために仕方なくインストールしてたjQueryもLaravel8以降は完全に撤去されたので一切使うことはない。

最初からやってることが間違ってる質問に回答しても誰の役にも立たないので回答しない。
初心者なら「jQueryでajax」なんて変な使い方する前にbladeだけの普通のLaravelの使い方を覚えるのが先。

1

Your answer might help someone💌